/*****************************************************************************
 * decode_mpeg.c: MPEG decoder example
 *----------------------------------------------------------------------------
 * Copyright (C) 2001-2011 VideoLAN
 * $Id: decode_mpeg.c 104 2005-03-21 13:38:56Z massiot $
 *
 * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
 *          Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *----------------------------------------------------------------------------
 *
 *****************************************************************************/

#include "config.h"


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>

#ifdef HAVE_SYS_TIME_H
#define HAVE_GETTIMEOFDAY 1

#include <sys/time.h>

#endif

#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>

#include <getopt.h>

#include <android/log.h>

#define TAG "dvbpsi"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

//#include "connect.h"

#if defined(HAVE_INTTYPES_H)

#include <inttypes.h>

#elif defined(HAVE_STDINT_H)
#include <stdint.h>
#endif

#include <dvbpsi.h>
#include <psi.h>
#include <pat.h>
#include <descriptor.h>
#include <pmt.h>
#include <descriptors/dr.h>
#include <jni.h>


#define SYSTEM_CLOCK_DR 0x0B
#define MAX_BITRATE_DR 0x0E
#define STREAM_IDENTIFIER_DR 0x52
#define SUBTITLING_DR 0x59

/*****************************************************************************
 * General typdefs
 *****************************************************************************/
typedef int vlc_bool_t;
#define VLC_FALSE 0
#define VLC_TRUE  1

typedef int64_t mtime_t;

#define REPORT_PCR 0 /* report PCR arrival and values */
#define REPORT_UDP 1 /* report UDP packet arrival (7 ts packets) */

/*****************************************************************************
 * TS stream structures
 *----------------------------------------------------------------------------
 * PAT pid=0
 * - refers to N PMT's with pids known PAT
 *  PMT 0 pid=X stream_type
 *  PMT 1 pid=Y stream_type
 *  PMT 2 pid=Z stream_type
 *  - a PMT refers to N program_streams with pids known from PMT
 *   PID A type audio
 *   PID B type audio
 *   PID C type audio .. etc
 *   PID D type video
 *   PID E type teletext
 *****************************************************************************/

typedef struct {
    dvbpsi_t *handle;

    int i_pat_version;
    int i_ts_id;
} ts_pat_t;

typedef struct ts_pid_s {
    int i_pid;

    vlc_bool_t b_seen;
    int i_cc;   /* countinuity counter */

    vlc_bool_t b_pcr;  /* this PID is the PCR_PID */
    mtime_t i_pcr;  /* last know PCR value */
} ts_pid_t;

typedef struct ts_pmt_s {
    dvbpsi_t *handle;

    int i_number; /* i_number = 0 is actually a NIT */
    int i_pmt_version;
    ts_pid_t *pid_pmt;
    ts_pid_t *pid_pcr;
} ts_pmt_t;

typedef struct {
    ts_pat_t pat;

    int i_pmt;
    ts_pmt_t pmt;

    ts_pid_t pid[8192];
} ts_stream_t;

#define PACKET_SIZE 192

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static void DumpPMT(void *p_data, dvbpsi_pmt_t *p_pmt);

/*****************************************************************************
 * ReadPacket
 *****************************************************************************/

static long posioton = 0;
static int ReadPacket(int i_fd, uint8_t *p_dst) {
    int i = PACKET_SIZE - 1;
    int i_rc = 1;

    p_dst[0] = 0;

    while ((p_dst[0] != 0x47) && (i_rc > 0)) {
        i_rc = read(i_fd, p_dst, 1);

        if (i_rc >0)
            posioton = posioton + i_rc;

    }
    LOGV("read data at: %ld",posioton);
    while ((i != 0) && (i_rc > 0)) {
        i_rc = read(i_fd, p_dst + PACKET_SIZE - i, i);

        if (i_rc >0)
            posioton = posioton + i_rc;

        if (i_rc >= 0)
            i -= i_rc;
    }

    return (i_rc <= 0) ? i_rc : PACKET_SIZE;
}


static void message(dvbpsi_t *handle, const dvbpsi_msg_level_t level, const char *msg) {
    switch (level) {
        case DVBPSI_MSG_ERROR:
            LOGE("Error: %s", msg);
            break;
        case DVBPSI_MSG_WARN:
            LOGW("Warning: %s", msg);
            break;
        case DVBPSI_MSG_DEBUG:
            LOGD("Debug: %s", msg);
            break;
        default: /* do nothing */
            LOGW("Warning: %s", msg);
            return;
    }
//    LOGE( "%s\n", msg);
}

/*****************************************************************************
 * DumpPAT
 *****************************************************************************/
static void DumpPAT(void *p_data, dvbpsi_pat_t *p_pat) {
    dvbpsi_pat_program_t *p_program = p_pat->p_first_program;
    ts_stream_t *p_stream = (ts_stream_t *) p_data;

    if (p_stream->pmt.handle) {
        LOGE("freeing old PMT\n");
        dvbpsi_pmt_detach(p_stream->pmt.handle);
        dvbpsi_delete(p_stream->pmt.handle);
        p_stream->pmt.handle = NULL;
    }

    p_stream->pat.i_pat_version = p_pat->i_version;
    p_stream->pat.i_ts_id = p_pat->i_ts_id;

    LOGE("\n");
    LOGE("New PAT\n");
    LOGE("  transport_stream_id : %d\n", p_pat->i_ts_id);
    LOGE("  version_number      : %d\n", p_pat->i_version);
    LOGE("    | program_number @ [NIT|PMT]_PID\n");
    while (p_program) {
        if (p_stream->pmt.handle) {
            dvbpsi_pmt_detach(p_stream->pmt.handle);
            dvbpsi_delete(p_stream->pmt.handle);
            p_stream->pmt.handle = NULL;
        }
        p_stream->i_pmt++;
        p_stream->pmt.i_number = p_program->i_number;
        p_stream->pmt.pid_pmt = &p_stream->pid[p_program->i_pid];
        p_stream->pmt.pid_pmt->i_pid = p_program->i_pid;
        p_stream->pmt.handle = dvbpsi_new(&message, DVBPSI_MSG_DEBUG);
        if (p_stream->pmt.handle == NULL) {
            LOGE("could not allocate new dvbpsi_t handle\n");
            break;
        }
        if (!dvbpsi_pmt_attach(p_stream->pmt.handle, p_program->i_number, DumpPMT, p_stream)) {
            dvbpsi_delete(p_stream->pmt.handle);
            LOGE("could not attach PMT\n");
            break;
        }
        LOGE("    | %14d @ 0x%x (%d)\n",
             p_program->i_number, p_program->i_pid, p_program->i_pid);
        p_program = p_program->p_next;
    }
    LOGE("  active              : %d\n", p_pat->b_current_next);
    dvbpsi_pat_delete(p_pat);
}

/*****************************************************************************
 * GetTypeName
 *****************************************************************************/
static char const *GetTypeName(uint8_t type) {
    switch (type) {
        case 0x00:
            return "Reserved";
        case 0x01:
            return "ISO/IEC 11172 Video";
        case 0x02:
            return "ISO/IEC 13818-2 Video";
        case 0x03:
            return "ISO/IEC 11172 Audio";
        case 0x04:
            return "ISO/IEC 13818-3 Audio";
        case 0x05:
            return "ISO/IEC 13818-1 Private Section";
        case 0x06:
            return "ISO/IEC 13818-1 Private PES data packets";
        case 0x07:
            return "ISO/IEC 13522 MHEG";
        case 0x08:
            return "ISO/IEC 13818-1 Annex A DSM CC";
        case 0x09:
            return "H222.1";
        case 0x0A:
            return "ISO/IEC 13818-6 type A";
        case 0x0B:
            return "ISO/IEC 13818-6 type B";
        case 0x0C:
            return "ISO/IEC 13818-6 type C";
        case 0x0D:
            return "ISO/IEC 13818-6 type D";
        case 0x0E:
            return "ISO/IEC 13818-1 auxillary";
        default:
            if (type < 0x80)
                return "ISO/IEC 13818-1 reserved";
            else
                return "User Private";
    }
}

/*****************************************************************************
 * DumpMaxBitrateDescriptor
 *****************************************************************************/
static void DumpMaxBitrateDescriptor(dvbpsi_max_bitrate_dr_t *bitrate_descriptor) {
    LOGE("Bitrate: %d\n", bitrate_descriptor->i_max_bitrate);
}

/*****************************************************************************
 * DumpSystemClockDescriptor
 *****************************************************************************/
static void DumpSystemClockDescriptor(dvbpsi_system_clock_dr_t *p_clock_descriptor) {
    LOGE("External clock: %s, Accuracy: %E\n",
         p_clock_descriptor->b_external_clock_ref ? "Yes" : "No",
         p_clock_descriptor->i_clock_accuracy_integer *
         pow(10.0, -(double) p_clock_descriptor->i_clock_accuracy_exponent));
}

/*****************************************************************************
 * DumpStreamIdentifierDescriptor
 *****************************************************************************/
static void DumpStreamIdentifierDescriptor(dvbpsi_stream_identifier_dr_t *p_si_descriptor) {
    LOGE("Component tag: %d\n",
         p_si_descriptor->i_component_tag);
}

/*****************************************************************************
 * DumpSubtitleDescriptor
 *****************************************************************************/
static void DumpSubtitleDescriptor(dvbpsi_subtitling_dr_t *p_subtitle_descriptor) {
    int a;

    LOGE("%d subtitles,\n", p_subtitle_descriptor->i_subtitles_number);
    for (a = 0; a < p_subtitle_descriptor->i_subtitles_number; ++a) {
        LOGE("       | %d - lang: %c%c%c, type: %d, cpid: %d, apid: %d\n", a,
             p_subtitle_descriptor->p_subtitle[a].i_iso6392_language_code[0],
             p_subtitle_descriptor->p_subtitle[a].i_iso6392_language_code[1],
             p_subtitle_descriptor->p_subtitle[a].i_iso6392_language_code[2],
             p_subtitle_descriptor->p_subtitle[a].i_subtitling_type,
             p_subtitle_descriptor->p_subtitle[a].i_composition_page_id,
             p_subtitle_descriptor->p_subtitle[a].i_ancillary_page_id);
    }
}

/*****************************************************************************
 * DumpDescriptors
 *****************************************************************************/
static void DumpDescriptors(const char *str, dvbpsi_descriptor_t *p_descriptor) {
    int i;

    while (p_descriptor) {
        LOGE("%s 0x%02x : ", str, p_descriptor->i_tag);
        switch (p_descriptor->i_tag) {
            case SYSTEM_CLOCK_DR:
                DumpSystemClockDescriptor(dvbpsi_DecodeSystemClockDr(p_descriptor));
                break;
            case MAX_BITRATE_DR:
                DumpMaxBitrateDescriptor(dvbpsi_DecodeMaxBitrateDr(p_descriptor));
                break;
            case STREAM_IDENTIFIER_DR:
                DumpStreamIdentifierDescriptor(dvbpsi_DecodeStreamIdentifierDr(p_descriptor));
                break;
            case SUBTITLING_DR:
                DumpSubtitleDescriptor(dvbpsi_DecodeSubtitlingDr(p_descriptor));
                break;
            default:
                LOGE("\"");
                for (i = 0; i < p_descriptor->i_length; i++)
                    LOGE("%c", p_descriptor->p_data[i]);
                LOGE("\"\n");
        }
        p_descriptor = p_descriptor->p_next;
    }
}

/*****************************************************************************
 * DumpPMT
 *****************************************************************************/
static void DumpPMT(void *p_data, dvbpsi_pmt_t *p_pmt) {
    dvbpsi_pmt_es_t *p_es = p_pmt->p_first_es;
    ts_stream_t *p_stream = (ts_stream_t *) p_data;

    p_stream->pmt.i_pmt_version = p_pmt->i_version;
    p_stream->pmt.pid_pcr = &p_stream->pid[p_pmt->i_pcr_pid];
    p_stream->pid[p_pmt->i_pcr_pid].b_pcr = VLC_TRUE;

    LOGE("\n");
    LOGE("New active PMT\n");
    LOGE("  program_number : %d\n", p_pmt->i_program_number);
    LOGE("  version_number : %d\n", p_pmt->i_version);
    LOGE("  PCR_PID        : 0x%x (%d)\n", p_pmt->i_pcr_pid, p_pmt->i_pcr_pid);
    DumpDescriptors("    ]", p_pmt->p_first_descriptor);
    LOGE("    | type @ elementary_PID\n");
    while (p_es) {
        LOGE("    | 0x%02x (%s) @ 0x%x (%d)\n", p_es->i_type, GetTypeName(p_es->i_type),
             p_es->i_pid, p_es->i_pid);
        DumpDescriptors("    |  ]", p_es->p_first_descriptor);
        p_es = p_es->p_next;
    }
    dvbpsi_pmt_delete(p_pmt);
}


/*****************************************************************************
 * main
 *****************************************************************************/
int enter_point(int i_argc, char *pa_argv[]) {

    int i_fd = -1;

    int i_old_cc = -1;
    uint32_t i_bytes = 0; /* bytes transmitted between PCR's */
    char *filename = NULL;

    uint8_t *p_data = NULL;
    ts_stream_t *p_stream = NULL;
    int i_len = 0;
    bool b_verbose = true;

    filename = "/mnt/sata/tmp/00983.m2ts";
//    filename = "/mnt/sata/tmp/1080.ts";
    /* initialize */
    if (filename) {
        i_fd = open(filename, 0);
        p_data = (uint8_t *) malloc(sizeof(uint8_t) * PACKET_SIZE);
        if (!p_data)
            goto out_of_memory;
    }

    p_stream = (ts_stream_t *) malloc(sizeof(ts_stream_t));
    if (!p_stream)
        goto out_of_memory;
    memset(p_stream, 0, sizeof(ts_stream_t));

    /* Read first packet */
    if (filename)
        i_len = ReadPacket(i_fd, p_data);

    p_stream->pat.handle = dvbpsi_new(&message, DVBPSI_MSG_DEBUG);
    if (p_stream->pat.handle == NULL)
        goto dvbpsi_out;
    if (!dvbpsi_pat_attach(p_stream->pat.handle, DumpPAT, p_stream))
        goto dvbpsi_out;

    /* Enter infinite loop */
    int count = 0;
    while (i_len > 0) {
        int i = 0;

        {
            uint8_t *p_tmp = &p_data[i];
            uint16_t i_pid = ((uint16_t) (p_tmp[1] & 0x1f) << 8) + p_tmp[2];
            int i_cc = (p_tmp[3] & 0x0f);
            vlc_bool_t b_adaptation = (p_tmp[3] & 0x20); /* adaptation field */
            vlc_bool_t b_discontinuity_seen = VLC_FALSE;

            if (p_data[i] != 0x47) /* no sync skip this packet */
            {
                LOGE("Missing TS sync word, skipping PACKET_SIZE bytes\n");
                break;
            }

            if (i_pid == 0x1FFF) { /* null packet - TS content undefined */
                LOGE("null packet - TS content undefined\n");
                continue;
            }

            if (i_pid == 0x0)
                dvbpsi_packet_push(p_stream->pat.handle, p_tmp);
            else if (p_stream->pmt.pid_pmt && i_pid == p_stream->pmt.pid_pmt->i_pid)
                dvbpsi_packet_push(p_stream->pmt.handle, p_tmp);

            LOGE("--------------read (pid %d)\n", i_pid);


            /* Remember PID */
            if (!p_stream->pid[i_pid].b_seen) {
                p_stream->pid[i_pid].b_seen = VLC_TRUE;
                i_old_cc = i_cc;
                p_stream->pid[i_pid].i_cc = i_cc;
            } else {
                /* Check continuity counter */
                int i_diff = 0;

                i_diff = i_cc - (p_stream->pid[i_pid].i_cc + 1) % 16;
                b_discontinuity_seen = (i_diff != 0);

                /* Update CC */
                i_old_cc = p_stream->pid[i_pid].i_cc;
                p_stream->pid[i_pid].i_cc = i_cc;
            }

            /* Handle discontinuities if they occurred,
             * according to ISO/IEC 13818-1: DIS pages 20-22 */
            if (b_adaptation) {
                vlc_bool_t b_discontinuity_indicator = (p_tmp[5] & 0x80);
                vlc_bool_t b_random_access_indicator = (p_tmp[5] & 0x40);
                vlc_bool_t b_pcr = (p_tmp[5] & 0x10);  /* PCR flag */

                if (b_discontinuity_indicator)
                    LOGE("Discontinuity indicator (pid %d)\n", i_pid);
                if (b_random_access_indicator)
                    LOGE("Random access indicator (pid %d)\n", i_pid);

                /* Dump PCR */
                if (b_pcr && (p_tmp[4] >= 7)) {
                    mtime_t i_pcr;  /* 33 bits */

                    i_pcr = (((mtime_t) p_tmp[6] << 25) |
                             ((mtime_t) p_tmp[7] << 17) |
                             ((mtime_t) p_tmp[8] << 9) |
                             ((mtime_t) p_tmp[9] << 1) |
                             ((mtime_t) p_tmp[10] >> 7)) / 90;
                    p_stream->pid[i_pid].i_pcr = i_pcr;

                    if (b_discontinuity_indicator) {
                        /* cc discontinuity is expected */
                        LOGE("Server signalled the continuity counter discontinuity\n");
                        /* Discontinuity has been handled */
                        b_discontinuity_seen = VLC_FALSE;
                    }
                }
            }

//            if (b_discontinuity_seen) {
//                LOGE("Continuity counter discontinuity (pid %d found %d expected %d)\n",
//                     i_pid, p_stream->pid[i_pid].i_cc, i_old_cc + 1);
//                /* Discontinuity has been handled */
//                b_discontinuity_seen = VLC_FALSE;
//            }

            if (count < 10)
                return 1;

            count = count - 1;
        }

        /* Read next packet */
        if (filename)
            i_len = ReadPacket(i_fd, p_data);

        count = count - 1;
        if (count <= 0)
            return 0;
    }

    if (p_stream->pmt.handle) {
        dvbpsi_pmt_detach(p_stream->pmt.handle);
        dvbpsi_delete(p_stream->pmt.handle);
    }
    if (p_stream->pat.handle) {
        dvbpsi_pat_detach(p_stream->pat.handle);
        dvbpsi_delete(p_stream->pat.handle);
    }

    /* clean up */
    if (filename)
        close(i_fd);


    if (p_data) free(p_data);
    if (filename) free(filename);

    /* free other stuff first ;-)*/
    if (p_stream) free(p_stream);
    return EXIT_SUCCESS;

    out_of_memory:
    LOGE("Out of memory\n");

    dvbpsi_out:
    if (p_stream && p_stream->pat.handle) {
        dvbpsi_pat_detach(p_stream->pat.handle);
        dvbpsi_delete(p_stream->pat.handle);
    }
    return EXIT_FAILURE;
}
JNIEXPORT

#include <string.h>

static void hex_dump_internal(const uint8_t *buf, int size)
{
    int len, i, j, c;

    char str1[512] = { 0 };

    for (i = 0; i < size; i += 128) {
        len = size - i;
        if (len > 128)
            len = 128;
        sprintf(str1,"%08x ", i);
        for (j = 0; j < 128; j++) {
            if (j < len)
                sprintf(str1,"%s %02x", str1, buf[i + j]);
            else
                ;
        }
    }
}

JNIEXPORT void JNICALL
Java_com_jimmy_libdvbpsi_MainActivity_enter(JNIEnv *env, jobject thiz) {

    enter_point(1, "-help");
}